from djcontrol import *
from Tkinter import *

import djcontrolutil
from djbutton import DjButton
from djfield import DjField
from djframe import DjFrame
from djimage import DjImage
from djinput import DjInput
from djlabel import DjLabel
from djlistbox import DjListbox
from djmenu import DjMenu
from djoptionmenu import DjOptionMenu

class DjWindow(DjControl):
	"""
	Properties:
	*bgcolor* - Background color of the window. RGB triplet, such as (255,0,0) for pure red.
	*borderwidth* - Integer, the width of the border.
	*cursor* - String with cursor name. See http://infohost.nmt.edu/tcc/help/pubs/tkinter/cursors.html.
	*height* - Height, in pixels. Note that if the pack geometry is used, setting this may not take effect.
	*width* - Width, in pixels. Note that if the pack geometry is used, setting this may not take effect.
	*menu* - Menubar of the window.
	*borderstyle* - Border style. One of 'sunken','raised','groove','ridge','flat'. Default is 'raised'. Set to 'sunken' and it will appear pressed.
	
	*transparency* - Transparency of window from 0.0 to 1.0. May not be supported in all operating systems.
	*enabled* - If false, disables entire window. May not be supported in all operating systems.
	*topmost* - Window appears above all windows. May not be supported in all operating systems.
	*title* - Text to appear in title.
	*onclose* - Provide a callback function to be called if the window closes. If the function returns False, this cancels the window from being closed.
	*onfocus* - Provide a callback function to be called if the window receives focus. May not be supported in all operating systems. Apparently doesn't work in Windows.
	*menubar* - The menu object. See DjMenu.
	"""
	
	# Note that only names beginning with '_', or the special case basecontrol, should be used.
	_autoProperties = None
	_onclose = None
	_onfocus = None
	_menubar = None
	basecontrol = 0
	
	# For widget packing
	_packOrientation = 'H'
	_containers = None
	
	_images = None #place to store image objects
	
	def __init__(self, basecontrol, **options):
		
		self.basecontrol = basecontrol
		self._autoProperties = {
		# Property name,  Tkinter property name, SET wrapper, GET wrapper
		'bgcolor': ('background',djcontrolutil.colorToString,djcontrolutil.stringToColor),
		'borderwidth': ('borderwidth',None,None),
		'cursor': ('cursor',None,None),
		'width': ('width',None,None),
		'height': ('height',None,None),
		'menu': ('menu',None,None),
		'borderstyle': ('relief', djcontrolutil.stringToRelief,djcontrolutil.reliefToString),
			
		}
		self.set_many(**options)
			
		# For widget packing
		self._containers = [self]
		
	
	##Widget Packing	
	def set_orientation(self, marker):
		"""Set orientation when packing into this window."""
		if marker==H: self._packOrientation = H
		elif marker==V: self._packOrientation = V
		else: raise ValueError,'Marker should be H or V'
	
	
	def _super_add(self, classObj, arParams, parent=None, place_now=True, fill=False, auto_size=False, pad=None, anchor=None, **kwargs):
		'''Create a widget and, by default add it to window.'''
		if parent==None:
			parent = self._containers[-1]
		widgetObj = classObj(parent, *arParams, **kwargs)
		if place_now:
			widgetObj.add_to_window(pack_orientation=parent._packOrientation, fill=fill, auto_size=auto_size, pad=pad, anchor=anchor)
			
		return widgetObj
	
	def frame(self, orient=H,  **kwargs):
		"""Add frame to the form. By default, it is set it as the current container, although it will not be if place_now is False."""
		frame =  self._super_add(DjFrame, [orient], **kwargs)
		
		#place_now is on by default.
		if ('place_now' not in kwargs) or ('place_now' in kwargs and kwargs['place_now']): 
			self._containers.append(frame) #Use this as the current container, until an endframe() call
		return frame
	
	def endframe(self):
		"""End the frame as the current container."""
		self._containers.pop()
	
	def button(self, strLabel='',fncallback=None, **kwargs):
		"""Add Button widget to window."""
		return self._super_add(DjButton, [strLabel, fncallback], **kwargs)
	
	def image(self,  **kwargs):
		"""Add Image widget to window."""
		img = self._super_add(DjImage, [], **kwargs)
		
		#Keep a reference to the image so that it is not garbage collected(!)
		if self._images==None: self._images = []
		self._images.append(img)
		return img
	
	def field(self, strDefaultText='',  **kwargs):
		"""Add Field widget to window."""
		return self._super_add(DjField, [strDefaultText], **kwargs)
		
	def label(self, strText='',  **kwargs):
		"""Add Label widget to window."""
		return self._super_add(DjLabel, [strText], **kwargs)
		
	def input(self, strText='',  **kwargs):
		"""Add Input widget to window."""
		return self._super_add(DjInput, [strText], **kwargs)
		
	def listbox(self,  **kwargs):
		"""Add Listbox widget to window."""
		return self._super_add(DjListbox, [], **kwargs)
	
	def optionmenu(self,  **kwargs):
		"""Add OptionMenu widget to window."""
		return self._super_add(DjOptionMenu, [], **kwargs)
	
	
	
	
	##Other methods
	def set_aspect(self, min, max):
		"""Constrain the ratio between width and height. The ratio will be held between min and max. Apparently doesn't work in Windows(?)"""
		c = 1024.
		self.basecontrol.aspect(int(min*c),int(c),int(max*c),int(c))
	
	def set_style(self, strStyle):
		"""Set the style of the window. One of 'normal','dialog','tool','bare'. This can only be set once. """
		if strStyle=='normal':
			elf.basecontrol.attributes('-toolwindow',0)
		elif strStyle=='tool':
			#~ self.basecontrol.transient()
			self.basecontrol.attributes('-toolwindow',1)
		elif strStyle=='dialog':
			self.basecontrol.transient(self.basecontrol._root())
		elif strStyle=='bare':
			self.basecontrol.overrideredirect(1)
	
	def set_rect(self, x,y,xwidth,yheight):
		"""Set rectangle of the window. Uses screen coordinates."""
		s = "%dx%d%+d%+d" % (xwidth, yheight, x, y)
		self.basecontrol.geometry(s)
	def get_rect(self):
		"""Get rectangle of the window. Uses screen coordinates."""
		s = self.basecontrol.geometry()
		# This code from http://effbot.org/tkinterbook/wm.htm
		import re
		m = re.match(r"(\d+)x(\d+)([-+]\d+)([-+]\d+)", s)
		if not m: raise ValueError("failed to parse geometry string")
		a = map(int, m.groups())
		return (a[2],a[3],a[0], a[1])
	
	def set_size_limits(self, xmin=None, xmax=None, ymin=None, ymax=None):
		"""Set min and max size of the window, in pixels. By default, size is not limited."""
		self.basecontrol.maxsize(xmax,ymax)
		self.basecontrol.minsize(xmin,ymin)
		
	def set_resizeable(self, bxresizeable=True, byresizeable=True):
		"""Set whether the dimensions of the window can be changed."""
		if bxresizeable: bxresizeable=1
		else: bxresizeable=0
		if byresizeable: byresizeable=1
		else: byresizeable=0
		self.basecontrol.resizable(bxresizeable,byresizeable)

	def hide(self):
		"""Hides the window."""
		self.basecontrol.withdraw()
	def show(self):
		"""Shows the window."""
		self.basecontrol.deiconify()
	def maximize(self):
		toplevel = self.basecontrol.winfo_toplevel()
		try: # On MS Windows one can set the "zoomed" state.
			toplevel.wm_state('zoomed')
		except:
			w = root.winfo_screenwidth()
			h = root.winfo_screenheight() - 60
			geom_string = "%dx%d+0+0" % (w,h)
			toplevel.wm_geometry(geom_string)
		
	def minimize(self):
		"""Minimizes (iconify) the window."""
		self.basecontrol.iconify()
	
	
	def set_icon(self,icon, mask=-1):
		"""Set icon for the window. In Windows, you can provide a filename such as 'test.ico'. Also, you can provide a mask for transparency."""
		self.basecontrol.iconbitmap(icon)
		if mask!=-1: self.basecontrol.iconmask(mask)
			
	
		
	def set_shortcut_key(self, s, fn):
		"""Bind shortut key to window. Use syntax "Ctrl+O", "Alt+O", "Shift+O" and so on. """
		s = s.replace('+','-')
		if len(s)==1:
			if chr(s)>='A' and chr(s)<='Z':
				s = s.lower()
		elif '-' in s:
			d = {}
			spl = s.split('-')
			key = spl[-1] # the actual key press, like o
			for splpart in spl[0:-1]: #modifiers
				d[splpart]=True
			
			tkShortcut = '<'
			if 'Ctrl' in d or 'Control' in d: tkShortcut += 'Control-'
			if 'Alt' in d: tkShortcut += 'Alt-'
			#~ if 'Shift' in d: tkShortcut += 'Shift-'
			# Instead of <Control-Shift-o>, use <Control-O>
			if 'Shift' not in d:
				tkShortcut += key.lower()
			else:
				tkShortcut += key
			
			tkShortcut += '>'
			s = tkShortcut
		else:
			raise ValueError, 'Unrecognized key combination. Use syntax "Ctrl+O", "Alt+O", "Shift+O". Use the raw .bind method if necessary.'
		
		# Note that the callback function doesn't receive the event parameter. If you need this, use bind.
		self.basecontrol.bind(s, lambda evt: fn())
			
	def set_shortcut_keys(self, keys):
		"""Bind shortut keys to window. Keys in format {"Ctrl+O":myfunc, "Ctrl+Q":myfunc2} and so on. """
		for key,value in keys.iteritems():
			self.set_shortcut_key(key, value)
	
	_customProperties = ('transparency','enabled','topmost','title','onclose','onfocus','menubar')
	def _lookupCustomProperties(self, bSet, strAttribute, value=None):
		if strAttribute=='transparency':
			if bSet: 
				v = float(value)
				self.basecontrol.attributes('-alpha', v)
			else:  return self.basecontrol.attributes('-alpha')
		elif strAttribute=='enabled':
			if bSet: 
				if value: v=0
				else: v=1
				self.basecontrol.attributes('-disabled', v)
			else: 
				return not self.basecontrol.attributes('-disabled')
		elif strAttribute=='topmost':
			if bSet: 
				if value: v=1
				else: v=0
				self.basecontrol.attributes('-topmost', v)
			else:
				return not not self.basecontrol.attributes('-topmost')
		elif strAttribute=='title':
			if bSet: self.basecontrol.title(value)
			else: return self.basecontrol.title()
		elif strAttribute=='onclose':
			if bSet:
				if value==None:
					self.basecontrol.protocol('WM_DELETE_WINDOW', None)
				else:
					# if the callback returns False, keep the window open. Otherwise, close the window.
					def fnonclose(self=self, fn=value):
						res = fn()
						if res!=False: self.destroy()
					self.basecontrol.protocol('WM_DELETE_WINDOW', fnonclose)
				self._onclose = value
			else: return self._onclose
		elif strAttribute=='onfocus':
			if bSet:
				self.basecontrol.protocol('WM_TAKE_FOCUS', value)
				self._onfocus = value
			else: return self._onfocus
		elif strAttribute=='menubar':
			if bSet:
				basemenu = djcontrolutil.getbasecontrol(value) #We were either passed a DjMenu or a Tkinter menu
				self.basecontrol.config(menu=basemenu)
				self._menubar = value
			else:	return self._menubar
		else:
			raise AttributeError, strAttribute



def _stringToCompoundDict(): return {'no_label':NONE,'center':CENTER,'bottom':BOTTOM,'left':LEFT,'right':RIGHT,'top':TOP}
def _stringToCompound(value): return _stringToCompoundDict()[value] 
def _compoundToString(value): return djcontrolutil.dictInverse(_stringToCompoundDict())[value]
